#include "infobase.h"
#include <QString>
#include <QSysInfo>
#include <time.h>
#include <uchar.h>

using namespace std;

ostream& outputIndent(ostream& outs, int indent) {
    if (indent > 0) {
        while(indent > 0) {
            outs << "  ";
            --indent;
        }
    }
    return outs;
}

ostream& outputLineQString(ostream& outs, int indent, const QString& src) {
    outputIndent(outs, indent) << src.toStdString() << endl;
    return outs;
}

static ostream& outputMacroC(ostream& outs, int indent) {
#ifdef __STDC__
    outputLineQString(outs, indent, QString("__STDC__: %1").arg(__STDC__));
#endif
#ifdef __STDC_VERSION__
    outputLineQString(outs, indent, QString("__STDC_VERSION__: %1").arg(__STDC_VERSION__));
#endif
#ifdef __STDC_HOSTED__
    outputLineQString(outs, indent, QString("__STDC_HOSTED__: %1").arg(__STDC_HOSTED__));
#endif
    outputLineQString(outs, indent, QString("__DATE__: %1").arg(__DATE__));
    outputLineQString(outs, indent, QString("__TIME__: %1").arg(__TIME__));
#ifdef __TIMESTAMP__
    outputLineQString(outs, indent, QString("__TIMESTAMP__: %1").arg(__TIMESTAMP__));
#endif
    outputLineQString(outs, indent, QString("__FILE__: %1").arg(__FILE__));
    outputLineQString(outs, indent, QString("__LINE__: %1").arg(__LINE__));
    outputLineQString(outs, indent, QString("__func__: %1").arg(__func__));
#ifdef __FUNCDNAME__
    outputLineQString(outs, indent, QString("__FUNCDNAME__: %1").arg(__FUNCDNAME__));
#endif
#ifdef __FUNCSIG__
    outputLineQString(outs, indent, QString("__FUNCSIG__: %1").arg(__FUNCSIG__));
#endif
#ifdef __FUNCTION__
    outputLineQString(outs, indent, QString("__FUNCTION__: %1").arg(__FUNCTION__));
#endif
    // -- math.h --
#ifdef MATH_ERRNO
    outputLineQString(outs, indent, QString("MATH_ERRNO: %1").arg(MATH_ERRNO)); // Bitmask value with the possible values math_errhandling can take.
#endif
#ifdef MATH_ERREXCEPT
    outputLineQString(outs, indent, QString("MATH_ERREXCEPT: %1").arg(MATH_ERREXCEPT));
#endif
    // -- stdio.h --
    outputLineQString(outs, indent, QString("BUFSIZ: %1").arg(BUFSIZ)); // Buffer size
    outputLineQString(outs, indent, QString("FILENAME_MAX: %1").arg(FILENAME_MAX)); // Maximum length of file names
    outputLineQString(outs, indent, QString("FOPEN_MAX: %1").arg(FOPEN_MAX)); // Potential limit of simultaneous open streams
    outputLineQString(outs, indent, QString("L_tmpnam: %1").arg(L_tmpnam)); // Minimum length for temporary file name
    outputLineQString(outs, indent, QString("TMP_MAX: %1").arg(TMP_MAX)); // Number of temporary files
    // -- stdlib.h --
    outputLineQString(outs, indent, QString("EXIT_FAILURE: %1").arg(EXIT_FAILURE)); // Failure termination code
    outputLineQString(outs, indent, QString("EXIT_SUCCESS: %1").arg(EXIT_SUCCESS)); // Success termination code
    outputLineQString(outs, indent, QString("MB_CUR_MAX: %1").arg(MB_CUR_MAX)); // Maximum size of multibyte characters
    outputLineQString(outs, indent, QString("RAND_MAX: %1").arg(RAND_MAX)); // Maximum value returned by rand
    // -- time.h --
    outputLineQString(outs, indent, QString("CLOCKS_PER_SEC: %1").arg(CLOCKS_PER_SEC)); // Clock ticks per second
    // -- stdlib.h --
#ifdef __STD_UTF_16__
    outputLineQString(outs, indent, QString("__STD_UTF_16__: %1").arg(__STD_UTF_16__)); // If defined, values of type char16_t have UTF-16 encoding.
#endif
#ifdef __STD_UTF_32__
    outputLineQString(outs, indent, QString("__STD_UTF_32__: %1").arg(__STD_UTF_32__)); // If defined, values of type char32_t have UTF-32 encoding.
#endif
    outputLineQString(outs, indent, QString());
    return outs;
}

static ostream& outputMacroCpp(ostream& outs, int indent) {
#ifdef __cplusplus
    outputLineQString(outs, indent, QString("__cplusplus: %1").arg(__cplusplus));
#endif
#ifdef __STDCPP_THREADS__
    outputLineQString(outs, indent, QString("__STDCPP_THREADS__: %1").arg(__STDCPP_THREADS__));
#endif
    outputLineQString(outs, indent, QString());
    return outs;
}

static ostream& outputMacroQt(ostream& outs, int indent) {
    outputLineQString(outs, indent, QString("QT_VERSION: %1 # 0x%2").arg(QT_VERSION).arg(QT_VERSION, 0, 16));
    outputLineQString(outs, indent, QString("QT_VERSION_STR: %1").arg(QT_VERSION_STR));
    outputLineQString(outs, indent, QString("QT_VERSION_MAJOR: %1").arg(QT_VERSION_MAJOR));
    outputLineQString(outs, indent, QString("QT_VERSION_MINOR: %1").arg(QT_VERSION_MINOR));
    outputLineQString(outs, indent, QString("QT_VERSION_PATCH: %1").arg(QT_VERSION_PATCH));
    outputLineQString(outs, indent, QString("Q_BYTE_ORDER: %1 # 0x%2").arg(Q_BYTE_ORDER).arg(Q_BYTE_ORDER, 0, 16));
    // -- OS: qsystemdetection.h --
#ifdef Q_OS_WIN
    outputLineQString(outs, indent, QString("Q_OS_WIN: %1").arg(1));
#endif
#ifdef Q_OS_LINUX
    outputLineQString(outs, indent, QString("Q_OS_LINUX: %1").arg(1));
#endif
#ifdef QT_LINUXBASE
    outputLineQString(outs, indent, QString("QT_LINUXBASE: %1").arg(1));
#endif
#ifdef __LSB_VERSION__
    outputLineQString(outs, indent, QString("__LSB_VERSION__: %1").arg(__LSB_VERSION__));
#endif
#ifdef Q_OS_UNIX
    outputLineQString(outs, indent, QString("Q_OS_UNIX: %1").arg(1));
#endif
#ifdef Q_OS_BSD4
    outputLineQString(outs, indent, QString("Q_OS_BSD4: %1").arg(1));
#endif
#ifdef Q_OS_DARWIN
    outputLineQString(outs, indent, QString("Q_OS_DARWIN: %1").arg(1));
#endif
#ifdef Q_OS_MAC
    outputLineQString(outs, indent, QString("Q_OS_MAC: %1").arg(1));
#endif
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
    outputLineQString(outs, indent, QString("MAC_OS_X_VERSION_MIN_REQUIRED: %1").arg(MAC_OS_X_VERSION_MIN_REQUIRED));
#endif
#ifdef Q_OS_IOS
    outputLineQString(outs, indent, QString("Q_OS_IOS: %1").arg(1));
#endif
#ifdef Q_OS_ANDROID
    outputLineQString(outs, indent, QString("Q_OS_ANDROID: %1").arg(1));
#endif
#ifdef QT_PLATFORM_UIKIT
    outputLineQString(outs, indent, QString("QT_PLATFORM_UIKIT: %1").arg(1));
#endif
#ifdef Q_OS_WATCHOS
    outputLineQString(outs, indent, QString("Q_OS_WATCHOS: %1").arg(1));
#endif
#ifdef Q_OS_TVOS
    outputLineQString(outs, indent, QString("Q_OS_TVOS: %1").arg(1));
#endif
#ifdef Q_OS_WEBOS
    outputLineQString(outs, indent, QString("Q_OS_WEBOS: %1").arg(1));
#endif
#ifdef Q_OS_CYGWIN
    outputLineQString(outs, indent, QString("Q_OS_CYGWIN: %1").arg(1));
#endif
#ifdef Q_OS_AIX
    outputLineQString(outs, indent, QString("Q_OS_AIX: %1").arg(1));
#endif
    outputLineQString(outs, indent, QString());
    return outs;
}

static ostream& outputMacroSystem(ostream& outs, int indent) {
#ifdef DEBUG
    outputLineQString(outs, indent, QString("DEBUG: %1").arg(1));
#endif
#ifdef NDEBUG
    outputLineQString(outs, indent, QString("NDEBUG: %1").arg(1));
#endif
    // -- CPU --
#ifdef __i386__
    outputLineQString(outs, indent, QString("__i386__: %1").arg(__i386__));
#endif
#ifdef __x86_64__
    outputLineQString(outs, indent, QString("__x86_64__: %1").arg(__x86_64__));
#endif
#ifdef _M_IX86
    outputLineQString(outs, indent, QString("_M_IX86: %1").arg(_M_IX86));
#endif
#ifdef _M_X64
    outputLineQString(outs, indent, QString("_M_X64: %1").arg(_M_X64));
#endif
#ifdef _M_AMD64
    outputLineQString(outs, indent, QString("_M_AMD64: %1").arg(_M_AMD64));
#endif
#ifdef __AVX10_VER__
    outputLineQString(outs, indent, QString("__AVX10_VER__: %1").arg(__AVX10_VER__));
#endif
#ifdef __arm__
    outputLineQString(outs, indent, QString("__arm__: %1").arg(__arm__));
#endif
#ifdef __aarch64__
    outputLineQString(outs, indent, QString("__aarch64__: %1").arg(__aarch64__));
#endif
#ifdef _M_ARM
    outputLineQString(outs, indent, QString("_M_ARM: %1").arg(_M_ARM));
#endif
#ifdef __ARM_ARCH
    outputLineQString(outs, indent, QString("__ARM_ARCH: %1").arg(__ARM_ARCH));
#endif
#ifdef __mips__
    outputLineQString(outs, indent, QString("__mips__: %1").arg(__mips__));
#endif
#ifdef __powerpc__
    outputLineQString(outs, indent, QString("__powerpc__: %1").arg(__powerpc__));
#endif
    // -- gcc --
#ifdef __BIGGEST_ALIGNMENT__
    outputLineQString(outs, indent, QString("__BIGGEST_ALIGNMENT__: %1").arg(__BIGGEST_ALIGNMENT__));
#endif
#ifdef __GNUC__
    outputLineQString(outs, indent, QString("__GNUC__: %1").arg(__GNUC__));
#endif
#ifdef __GNUC_MINOR__
    outputLineQString(outs, indent, QString("__GNUC_MINOR__: %1").arg(__GNUC_MINOR__));
#endif
#ifdef __GNUC_PATCHLEVEL__
    outputLineQString(outs, indent, QString("__GNUC_PATCHLEVEL__: %1").arg(__GNUC_PATCHLEVEL__));
#endif
#ifdef __GXX_ABI_VERSION
    outputLineQString(outs, indent, QString("__GXX_ABI_VERSION: %1 # 0x%2").arg(__GXX_ABI_VERSION).arg(__GXX_ABI_VERSION, 0, 16));
#endif
#ifdef __ORDER_LITTLE_ENDIAN__
    outputLineQString(outs, indent, QString("__ORDER_LITTLE_ENDIAN__: %1 # 0x%2").arg(__ORDER_LITTLE_ENDIAN__).arg(__ORDER_LITTLE_ENDIAN__, 0, 16));
#endif
#ifdef __SIZEOF_POINTER__
    outputLineQString(outs, indent, QString("__SIZEOF_POINTER__: %1").arg(__SIZEOF_POINTER__));
#endif
#ifdef __VERSION__
    outputLineQString(outs, indent, QString("__VERSION__: %1").arg(__VERSION__));
#endif
    // -- Microsoft C/C++: https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-180 --
#ifdef __CLR_VER
    outputLineQString(outs, indent, QString("__CLR_VER: %1").arg(__CLR_VER));
#endif
#ifdef __cplusplus_cli
    outputLineQString(outs, indent, QString("__cplusplus_cli: %1").arg(__cplusplus_cli));
#endif
#ifdef __cplusplus_winrt
    outputLineQString(outs, indent, QString("__cplusplus_winrt: %1").arg(__cplusplus_winrt));
#endif
#ifdef _INTEGRAL_MAX_BITS
    outputLineQString(outs, indent, QString("_INTEGRAL_MAX_BITS: %1").arg(_INTEGRAL_MAX_BITS));
#endif
#ifdef _MSC_BUILD
    outputLineQString(outs, indent, QString("_MSC_BUILD: %1").arg(_MSC_BUILD));
#endif
#ifdef _MSC_BUILD
    outputLineQString(outs, indent, QString("_MSC_BUILD: %1").arg(_MSC_BUILD));
#endif
#ifdef _MSC_FULL_VER
    outputLineQString(outs, indent, QString("_MSC_FULL_VER: %1").arg(_MSC_FULL_VER));
#endif
#ifdef _MSC_VER
    outputLineQString(outs, indent, QString("_MSC_VER: %1").arg(_MSC_VER));
#endif
#ifdef _OPENMP
    outputLineQString(outs, indent, QString("_OPENMP: %1").arg(_OPENMP));
#endif
#ifdef _ATL_VER
    outputLineQString(outs, indent, QString("_ATL_VER: %1").arg(_ATL_VER));
#endif
#ifdef _MFC_VER
    outputLineQString(outs, indent, QString("_MFC_VER: %1").arg(_MFC_VER));
#endif
    // -- gcc --
#ifdef __clang__
    outputLineQString(outs, indent, QString("__clang__: %1").arg(__clang__));
#endif
#ifdef __clang_major__
    outputLineQString(outs, indent, QString("__clang_major__: %1").arg(__clang_major__));
#endif
#ifdef __clang_minor__
    outputLineQString(outs, indent, QString("__clang_minor__: %1").arg(__clang_minor__));
#endif
    // -- other --
#ifdef __MINGW32__
    outputLineQString(outs, indent, QString("__MINGW32__: %1").arg(__MINGW32__));
#endif
#ifdef __CYGWIN__
    outputLineQString(outs, indent, QString("__CYGWIN__: %1").arg(__CYGWIN__));
#endif
#ifdef __BORLANDC__
    outputLineQString(outs, indent, QString("__BORLANDC__: %1").arg(__BORLANDC__));
#endif
    outputLineQString(outs, indent, QString());
    return outs;
}

static ostream& outputQSysInfo(ostream& outs, int indent) {
    outputLineQString(outs, indent, QString("buildCpuArchitecture: %1").arg(QSysInfo::buildCpuArchitecture()));
    outputLineQString(outs, indent, QString("currentCpuArchitecture: %1").arg(QSysInfo::currentCpuArchitecture()));
    outputLineQString(outs, indent, QString("buildAbi: %1").arg(QSysInfo::buildAbi()));
    outputLineQString(outs, indent, QString("kernelType: %1").arg(QSysInfo::kernelType()));
    outputLineQString(outs, indent, QString("kernelVersion: %1").arg(QSysInfo::kernelVersion()));
    outputLineQString(outs, indent, QString("productType: %1").arg(QSysInfo::productType()));
    outputLineQString(outs, indent, QString("productVersion: %1").arg(QSysInfo::productVersion()));
    outputLineQString(outs, indent, QString("prettyProductName: %1").arg(QSysInfo::prettyProductName()));
    outputLineQString(outs, indent, QString("machineHostName: %1").arg(QSysInfo::machineHostName()));
    outputLineQString(outs, indent, QString("machineUniqueId: %1").arg(QString::fromLatin1(QSysInfo::machineUniqueId().toHex())));
    outputLineQString(outs, indent, QString("bootUniqueId: %1").arg(QString::fromLatin1(QSysInfo::bootUniqueId().toHex())));
    outputLineQString(outs, indent, QString());
    return outs;
}

std::ostream& outputBase(std::ostream& outs, int indent) {
    int indentNext = indent + 1;
    outputIndent(outs, indent) << "Macro-C:" << endl; outputMacroC(outs, indentNext);
    outputIndent(outs, indent) << "Macro-Cpp:" << endl; outputMacroCpp(outs, indentNext);
    outputIndent(outs, indent) << "Macro-Qt:" << endl; outputMacroQt(outs, indentNext);
    outputIndent(outs, indent) << "Macro-System:" << endl; outputMacroSystem(outs, indentNext);
    outputIndent(outs, indent) << "QSysInfo:" << endl; outputQSysInfo(outs, indentNext);
    return outs;
}
